%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%                 Sockets                   %%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:-use_module(library(sockets)).
:-use_module(library(random)).


port(60070).

% launch me in sockets mode
server:-
	port(Port),
	socket_server_open(Port, Socket),
	socket_server_accept(Socket, _Client, Stream, [type(text)]),
	write('Accepted connection'), nl,
	serverLoop(Stream),
	socket_server_close(Socket).

% wait for commands
serverLoop(Stream) :-
	repeat,
	read(Stream, ClientMsg),
	write('Received: '), write(ClientMsg), nl,
	parse_input(ClientMsg, MyReply),
	format(Stream, '~q.~n', [MyReply]),
	write('Wrote: '), write(MyReply), nl,
	flush_output(Stream),
	(ClientMsg == quit; ClientMsg == end_of_file), !.

parse_input(end_game(Arg1),Ans) :-
	end_game(Arg1,Ans).

parse_input(make_move(Arg1,Arg2,Arg3,Arg4),Ans) :-
	make_move(Arg1,Arg2,Arg3,Arg4,Ans).

parse_input(move_AI(Arg1,Arg2,Arg3),Ans) :-
	move_AI(Arg1,Arg2,Arg3,Ans).

parse_input(possibles(Arg1,Arg2,Arg3,Arg4),Ans) :-
	possibles(Arg1,Arg2,Arg3,Arg4,Ans).

parse_input(quit, ok-bye) :- !.




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% ELIMINAR DEPOIS
%
board([[1,0,0,1],[0,0,0,0],[0,0,0,0],[2,0,0,2]]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%    MOTOR DE JOGO  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
is_list(X) :-
        var(X), !,
        fail.
is_list([]).
is_list([_|T]) :-
        is_list(T).

%Contagem do numero de linhas ou colunas do tabuleiro
size(L,Num):-size(L,0,Num).
size([],Num,Num).
size([_|T],Size,Num):-
        Size2 is Size+1,
        size(T,Size2,Num).


%contagem de um dado elemento
%(Tabuleiro,Elemento,Resultado)
count([],_,0):-!.
count([Elem|T],Elem,Res) :- count(T,Elem,Res2), Res is Res2+1,!.
count([H|T],Elem,Res) :- H \= Elem, is_list(H),
                          count(H, Elem, Res1),
                          count(T, Elem, Res2), Res is Res1+Res2,!.
count([_|T],Elem,Res) :-count(T,Elem,Res).


%modificar o jogador
change_player(1,2).
change_player(2,1).



%modificar valor da celula para o definido
%(Tabuleiro,X,Y,NovoTabuleiro,ElementoAInserir)
create_piece([H|T],1,Y,[New|T],NewElem):-
        set_element_line(H,Y,New,NewElem).
create_piece([H|T],X,Y,[H|Elem],NewElem):-
        X>1,
        X2 is X-1,
        create_piece(T,X2,Y,Elem,NewElem).

set_element_line([_|T],1,[NewElem|T],NewElem).
set_element_line([H|T],X,[H|New],NewElem):-
        X>1,
        X2 is X-1,
        set_element_line(T,X2,New,NewElem).


%destruir(modificar para vazio) a celula
%(X,Y,Tabuleiro,NovoTabuleiro)
destroy_piece(X,Y,Board,Nlist):-
        create_piece(Board,X,Y,Nlist,0).


%retirar valor da celula
%(Tabuleiro,X,Y,Elemento)
get_piece([H|_],1,Y,Elem):-
        get_element_line(H,Y,Elem).
get_piece([_|T],X,Y,Elem):-
        X>1,
        X2 is X-1,
        get_piece(T,X2,Y,Elem).

get_element_line([H|_],1,H).
get_element_line([_|T],X,Elem):-
        X>1,
        X2 is X-1,
        get_element_line(T,X2,Elem).


%verifica se uma jogada e possivel
%(Jogador,Tabuleiro,X,Y,JogadaX,JogadaY,Resultado)
%Resultado=criar,mover,erro
possible(J,L,X,Y,X2,Y2,criar):-
        size(L,Size),
        X>0,X=<Size,X2>0,X2=<Size,
        Y>0,Y=<Size,Y2>0,Y2=<Size,
        get_piece(L,X,Y,Piece),
        get_piece(L,X2,Y2,0),
        Piece=J,
        DeltaX is X2-X,
        DeltaY is Y2-Y,
        DeltaX =< 1, DeltaX >= -1,
        DeltaY =< 1, DeltaY >= -1,!.
possible(J,L,X,Y,X2,Y2,mover):-
        size(L,Size),
        X>0,X=<Size,X2>0,X2=<Size,
        Y>0,Y=<Size,Y2>0,Y2=<Size,
        get_piece(L,X,Y,Piece),
        get_piece(L,X2,Y2,0),
        Piece=J,
        DeltaX is X2-X,
        DeltaY is Y2-Y,
        DeltaX =< 2, DeltaX >= -2,
        DeltaY =< 2, DeltaY >= -2,!.
possible(_,_,_,_,_,_,erro).





all_possibles(S,_,_,_,_,_,Y,[]):-Yc is S+1,Yc=Y,!.
all_possibles(Size,J,Tab,X,Y,Xc,Yc,[H|T]):-
	%nl,write('X:'),write(Xc),nl,write('Y:'),write(Yc),
	possible(J,Tab,X,Y,Yc,Xc,Resp),
	((Resp=criar,H=1)|
	(Resp=mover,H=2)|
	(Resp=erro,H=0)),
	Xc2 is Xc+1,
	((Xc2>Size,Xcf is 1,Ycf is Yc+1)|
	(Xc2=<Size,Xcf is Xc2,Ycf is Yc)),!,
	all_possibles(Size,J,Tab,X,Y,Xcf,Ycf,T).


%(Tabuleiro,Elemento,Resultado)
print_list([]):-!.
print_list([H|T]) :- is_list(H),!,
                          print_list(H),
                          print_list(T).
print_list([H|T]) :- write(H),print_list(T),!.


%com os sockets
possibles(J,Tab,X,Y,Ans):-
	size(Tab,Size),
	all_possibles(Size,J,Tab,X,Y,1,1,Ans).


make_move(J,X,Y,Tab,NewTab):-
	change_player(J,Jn),
	change_adjacent(Jn,J,Y,X,1,Tab,NewTab).

move_AI(J,Dif,Tab,FinalBoard):-
	playAI(J,Dif,Tab,FinalBoard,0).

end_game(Tab,Ans):-
	count(Tab,0,Res),
	Res\=0,!,Ans='NO'.

end_game(_,Ans):-
	Ans='YES'.


%modificar valores das celulas circundates para o definido

%change_adjacent(Jogador,X,Y,Tabuleiro,NovoTabuleiro)
trychange(J,Jn,J,Jn):-!.
trychange(_,_,H,H).

change_adjacentAI(J,X,Y,Tab,Newtab):-
        change_player(J,Jn),
        change_adjacent(Jn,J,X,Y,1,Tab,Newtab).


change_adjacent(_,_,_,_,_,[],[]):-!.
change_adjacent(J,Jn,X,Y,Xc,[H|T],[New|Tn]):-
        DeltaX is X-Xc,
        DeltaX =< 1, DeltaX >= -1,!,
        change_adjacent_line(J,Jn,Y,1,H,New),
        Xc2 is Xc+1,
        change_adjacent(J,Jn,X,Y,Xc2,T,Tn).
change_adjacent(J,Jn,X,Y,Xc,[H|T],[H|Tn]):-
        Xc2 is Xc+1,
        change_adjacent(J,Jn,X,Y,Xc2,T,Tn).


change_adjacent_line(_,_,_,_,[],[]):-!.
change_adjacent_line(J,Jn,Y,Yc,[H|T],[New|Ta]):-
        DeltaY is Y-Yc,
        DeltaY =< 1, DeltaY >= -1,!,
        trychange(J,Jn,H,New),
        Yc2 is Yc+1,
        change_adjacent_line(J,Jn,Y,Yc2,T,Ta).
change_adjacent_line(J,Jn,Y,Yc,[H|T],[H|Ta]):-
        Yc2 is Yc+1,
        change_adjacent_line(J,Jn,Y,Yc2,T,Ta).




%jogada do pc
%(Player,Dificuldade,Tabuleiro,NovoTabuleiro,0)
playAI(_,_,Board,Board,1000):-!.
playAI(Elem,Dif,Board,NewFinalBoard,Count):-
               count(Board,Elem,ContagemPecas),
               ContagemPecas>0,
               !,
               Count2 is Count+1,
               size(Board,Size),
               Size2 is Size+1,
               random(1, Size2, Random),
               random(1, Size2, Random2),
               %write(Random),write('   '),write(Random2),nl,
               get_piece(Board,Random,Random2,Piece),
               ((Piece=Elem,all_possible(Elem,Dif,Board,Random,Random2,Size,Size,Size,Result,X2,Y2),
               ((Result=true,possible(Elem,Board,Random,Random2,X2,Y2,Res),%write('ESTA: '),write(Random),nl,write(Random2),write(Res),nl,
                           ((Res=criar,create_piece(Board,X2,Y2,NBoard,Elem),change_adjacentAI(Elem,X2,Y2,NBoard,NewFinalBoard))|
                           (Res=mover,create_piece(Board,X2,Y2,NBoard,Elem),destroy_piece(Random,Random2,NBoard,NewBoard),change_adjacentAI(Elem,X2,Y2,NewBoard,NewFinalBoard))))|
               (Result=false,playAI(Elem,Dif,Board,NewFinalBoard,Count2))))|
               (Piece\=Elem,playAI(Elem,Dif,Board,NewFinalBoard,Count2))).

playAI(_,_,Board,Board,_).



all_possible(Elem,easy,Board,Xorigem,Yorigem,IteratorX,IteratorY,Size,Result,Xdestino,Ydestino):-!,
                                                                               all_possible_easy(Elem,Board,Xorigem,Yorigem,IteratorX,IteratorY,Size,Result,Xdestino,Ydestino).
all_possible(Elem,hard,Board,Xorigem,Yorigem,IteratorX,IteratorY,Size,Result,Xdestino,Ydestino):-
                                                                               all_possible_hard(Elem,Board,Xorigem,Yorigem,IteratorX,IteratorY,Size,Result,0,0,Xdestino,Ydestino,-1,_).




%FACIL
% (3,Tabuleiro,OrigemJogadaX,OrigemJogadaY,SizeXTabuleiro,SizeYTabuleiro,SizeTabuleiro,R
% esultado,DestinoJogadaX,DestinoJogadaY)

all_possible_easy(_,_,_,_,_,0,_,false,_,_):-!.
all_possible_easy(Elem,Board,X,Y,Xc,Yc,_,true,Xc,Yc):-
          %write('A TESTAR'),write('Xc:'),write(Xc),nl,write('Yc:'),write(Yc),nl,
          possible(Elem,Board,X,Y,Xc,Yc,Res),
          Res\=erro,!.
all_possible_easy(Elem,Board,X,Y,Xc,Yc,Size,Result,X2,Y2):-
          %write('Xc:'),write(Xc),nl,write('Yc:'),write(Yc),nl,
          Xc2 is Xc-1,
          ((Xc2=0,Xcf is Size,Ycf is Yc-1)|
          (Xc2\=0,Xcf is Xc2,Ycf is Yc)),
          all_possible_easy(Elem,Board,X,Y,Xcf,Ycf,Size,Result,X2,Y2).


%DIFICIL
% (3,Tabuleiro,OrigemJogadaX,OrigemJogadaY,SizeXTabuleiro,SizeYTabuleiro,SizeTabuleiro
% Resultado,TempX(0),TempY(0),DestinoJogadaX,DestinoJogadaY,TempMax(-1),
% Max)
all_possible_hard(_,_,_,_,_,0,_,false,_,_,_,_,-1,_):-!.
all_possible_hard(_,_,_,_,_,0,_,true,X2,Y2,X2,Y2,Max,Max):-!.
all_possible_hard(Elem,Board,X,Y,Xc,Yc,Size,Result,_,_,X2,Y2,Max,Contagem3):-
          %write('A TESTAR'),write('Xc:'),write(Xc),nl,write('Yc:'),write(Yc),nl,
          %write('X2:'),write(X2),write('Y2:'),write(Y2),nl,
          %write('Max:'),write(Max),write('Contagem3:'),write(Contagem3),nl,
          %sleep(1),
          possible(Elem,Board,X,Y,Xc,Yc,Res),
          Res\=erro,
          change_adjacentAI(Elem,Xc,Yc,Board,FinalBoard),
          count(FinalBoard,Elem,Contagem2),
          ((Res=criar,Contagem4 is Contagem2 +1)|(Res=mover,Contagem4 is Contagem2)),
          %write('Max ANTES: '),write(Max),nl,
          %write('CONTAGEM2 ANTES: '),write(Contagem2),nl,
          Contagem4>=Max,!,
          %Contagem3=Contagem4,
          Xc2 is Xc-1,
          ((Xc2=0,Xcf is Size,Ycf is Yc-1)|
          (Xc2\=0,Xcf is Xc2,Ycf is Yc)),
          !,
          all_possible_hard(Elem,Board,X,Y,Xcf,Ycf,Size,Result,Xc,Yc,X2,Y2,Contagem4,Contagem3).
all_possible_hard(Elem,Board,X,Y,Xc,Yc,Size,Result,X2a,Y2a,X2,Y2,Max,Contagem):-
          %write('Xc:'),write(Xc),nl,write('Yc:'),write(Yc),nl,
          %sleep(1),
          Xc2 is Xc-1,
          ((Xc2=0,Xcf is Size,Ycf is Yc-1)|
          (Xc2\=0,Xcf is Xc2,Ycf is Yc)),
          all_possible_hard(Elem,Board,X,Y,Xcf,Ycf,Size,Result,X2a,Y2a,X2,Y2,Max,Contagem).










